(ns scripts.cljmon.monitor
  (:use scripts.cljmon.query
        scripts.cljmon.agent.client
        scripts.cljmon.logger
        util.mon
        util.utils))

(defn- decorate-remote-comment [host comment]
  (format "host: %s, %s" host comment))

(defn- decorate-remote-label [host label]
  (str label " @ " host))

(defn- update-remote-labels [host records]
  (for [r records]
    (assoc r :label (decorate-remote-label host (:label r)))))

(defn- add-default-unit [q opts]
  (let [unit (-> q
               :type
               {:system-info :memory-unit
                :process-info :memory-unit
                :network-io :network-unit
                :disk-io :disk-unit
                :disk-space :disk-unit}
               opts)]
    (if unit
      (merge {:unit unit} q)
      q)))

(defn- run-monitor [{:keys [queries pollrate pdh? winapi?] :as opts}]
  (let [by-agent (group-by #(let [a (:agent %)] (when a (agent-to-map a))) queries)
        local-queries (get by-agent nil)
        remote-queries (into {} (for [[a qq] (dissoc by-agent nil)]
                                  [(assoc a :id (uuid))
                                   (map #(add-default-unit % opts) qq)]))
        local-queries (translate-local-queries local-queries)
        local-queries (map #(add-default-unit % opts) local-queries)
        local-comments (collect-local-comments local-queries)
        local-headers (collect-local-headers local-queries)
        remotes (doall (for [[{:keys [url id] :as agent} queries] remote-queries]
                         (merge (register-query url id {:queries queries :pdh? pdh? :winapi? winapi?}) agent)))
        remote-comments (for [{:keys [host comments]} remotes] (map #(decorate-remote-comment host %) comments))
        remote-headers (for [{:keys [host headers]} remotes] (map #(decorate-remote-label host %) headers))
        comments (apply concat local-comments remote-comments)
        headers (apply concat local-headers remote-headers)]
    (println "Logging into" (log-name) "...")
    (log-clear)
    (binding [*cached-headings* (atom [])]
      (log-comments-and-headers comments headers)
      ;; records
      (binding [*output-buffer* (atom nil)
                *rollover-flag* (atom nil)]
        (loop []
          (let [results (apply concat
                          (collect-local-data local-queries)
                          (for [{:keys [url host id]} remotes]
                            (update-remote-labels host (execute-query url id))))
                timestamp (:value (as-one (filter #(= (:column %) :timestamp) results) "Timestamp"))]
            (log-records {:timestamp timestamp
                          :data results}))
          (when
            (try
              (Thread/sleep (* pollrate 1000))
              :continue
              (catch InterruptedException e
                (when *logfile* (println "Please check cljmon log at" (log-name)))
                nil))
            (pdh-update)
            (recur)))))
    (doseq [{:keys [url id]} remotes]
      (unregister-query url id))))


(defn start-monitor [{:keys [pdh? winapi? log] :as opts}]
  (run-in-new-query-session {:pdh? pdh? :winapi? winapi?}
    (if log
      (binding [*logfile* log]
        (run-monitor opts))
      (run-monitor opts))))

